#include <queue>
#include <list>
#include <pthread.h>
#include "monitorE.h"
#include "logger.h"

//Constructor
monitorE::monitorE(logger *log) {
	this->log = log;
	pthread_mutex_init(&lock, NULL);
	pthread_mutex_init(&completeLock, NULL);
	pthread_cond_init(&inputCheck, NULL);
	pthread_cond_init(&schedWait, NULL);
	liveClients = 0;
	newReqFlag = false;
	reqMade = 0;
	reqProc = 0;
	lastTrack = 0;
}

//Destructor
monitorE::~monitorE() {
	buffer[0] = 0;
	pthread_mutex_destroy(&lock);
	pthread_mutex_destroy(&completeLock);
	pthread_cond_destroy(&inputCheck);
	pthread_cond_destroy(&schedWait);
	//Destroy inputQ and outputQ when theyre DS have been declared	
}

//Add request. Takes a single request and adds it to input DS. 
bool monitorE::addRequest(request* req) {
	pthread_mutex_lock(&lock);
	newReqFlag = true;
	inputQ.push(req);
	reqMade ++;
	sprintf(buffer, "Request added. Client %d waiting....\n track num: %d and work: %d", req->getRequestingTid(), req->getTrackNum(), req->getWorkSize());
	log->debug(buffer, "c");
	buffer[0] = 0;
	pthread_cond_signal(&schedWait);
	pthread_cond_wait(&(req->CVWorkComplete), &lock);
	sprintf(buffer, "...Client %d finished waiting. Request finished.", req->getRequestingTid());
	log->debug(buffer, "c");
	buffer[0] = 0;
	pthread_mutex_unlock(&lock);
	return true;
	
}

//Schedules all requests in input DS by moving them to output DS. 
bool monitorE::scheduleRequests() {
	pthread_mutex_lock(&lock);
	bool ret_val = false;
	sprintf(buffer, "Scheduler entering scheduleRequest()"); 
	log->debug(buffer, "c");
	buffer[0] = 0;
	if (inputQ.empty() || !newReqFlag){
		sprintf(buffer, "Scheduler is waiting on clients, no new input.");
		log->debug(buffer, "c");
		buffer[0] = 0;
		pthread_cond_wait(&schedWait, &lock);
	}
	if(newReqFlag || (!newReqFlag && !inputQ.empty() && outputL.empty())) {
		if (outputL.empty()) {
			direction = !direction;
			sprintf(buffer, "direction change to : %d", direction);
			log->debug(buffer, "a");
			buffer[0] = 0;
		}
		request* curReq;
		queue<request*> sortQ;
		for (int i = 0; i < inputQ.size(); i++){
			curReq = inputQ.front();
			inputQ.pop();
			if (direction && curReq->getTrackNum() >= lastTrack){ //UP
				sortQ.push(curReq);
			} else if (!direction && curReq->getTrackNum() <= lastTrack) { //DOWN
				sortQ.push(curReq);
			} else {
				inputQ.push(curReq);
			}
		}
		request *newReq;
		list<request*>::iterator outReq;
		bool insrtd = false;
		int k = sortQ.size();
		for (int i = 0; i < k; i++){
			newReq = sortQ.front();
			sortQ.pop();
			if (direction){ //UP
				for (outReq = outputL.begin(); outReq != outputL.end(); outReq++){
					if(newReq->getTrackNum() <= (*outReq)->getTrackNum()){
						outputL.insert(outReq, newReq);
						insrtd = true;
						break;
					} 	
				}
				if (!insrtd) { 
					outputL.push_back(newReq);
				}
			} else { //DOWN
				for (outReq = outputL.begin(); outReq != outputL.end(); outReq++){
					if(newReq->getTrackNum() >= (*outReq)->getTrackNum()){
						outputL.insert(outReq, newReq);
						insrtd = true;
						break;
					} 
				}
				if (!insrtd) outputL.push_back(newReq);
			}
			insrtd = false;
		}
		ret_val = true;
		newReqFlag = false;
		sprintf(buffer, "Requests scheduled...");
		log->debug(buffer, "b");
		buffer[0] = 0;
		pthread_cond_signal(&inputCheck);
	} 
	pthread_mutex_unlock(&lock);
	return ret_val;
}

//Process request. Removes a single request from the output DS. 
request* monitorE::removeRequest() {
	//if FLAG condition variable wait until signaled
	pthread_mutex_lock(&lock);
	sprintf(buffer, "Worker entering removeRequest().");
	log->debug(buffer, "c");
	buffer[0] = 0;
	
	if (newReqFlag || (outputL.empty())) {
		sprintf(buffer, "Worker is waiting on scheduler; new requests made, or outputL empty.");
		log->debug(buffer, "a");
		buffer[0] = 0;
		pthread_cond_wait(&inputCheck, &lock);
	}
	request* temp;
	if (!outputL.empty()) {
		temp = outputL.front();
		outputL.pop_front();
		reqProc ++;
		lastTrack = temp->getTrackNum();
		sprintf(buffer, "Request processed");
		log->debug(buffer, "b");
		buffer[0] = 0;
	} else { 
		temp = NULL;
	}
	pthread_mutex_unlock(&lock);
	return(temp);
}
/*
 *Once worker has processed the request and modified the request object
 *(code not needed to be protected), pass back the request object to the monitor
 * so we can signal the reqest objects condition_variable, and wake up the client
 */
void monitorE::signalClient(request* req) {
	pthread_mutex_lock(&lock);
	pthread_cond_signal(&(req->CVWorkComplete));
	pthread_mutex_unlock(&lock);
}

bool monitorE::isWorkLeft() {
	pthread_mutex_lock(&lock);
	bool retVal = true;
	if (inputQ.empty() && outputL.empty()) {
		retVal = false;
	}
	pthread_mutex_unlock(&lock);
	return retVal;
}

/////////////////////////////////////////
// Methods for checking end conditions. 
/////////////////////////////////////////

int monitorE::getLiveClients() {
	//Note: When everything is working, we can get ride of the locks and see if it still works :P
	pthread_mutex_lock(&completeLock);
	int retVal = liveClients;
	pthread_mutex_unlock(&completeLock);
	return retVal;
}

void monitorE::decLiveClients() {
	pthread_mutex_lock(&completeLock);
	liveClients--;
	pthread_mutex_unlock(&completeLock);
}

void monitorE::incLiveClients() {
	pthread_mutex_lock(&completeLock);
	liveClients++;	
	pthread_mutex_unlock(&completeLock);
}

int monitorE::getReqDiff() {
	return (reqMade - reqProc);
}

int monitor::getReqProc() {
	return reqProc;
}
